package ac.il.technion.cs236369.hw5.core.servlets.users;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import ac.il.technion.cs236369.hw5.core.entities.Role;
import ac.il.technion.cs236369.hw5.core.entities.User;
import ac.il.technion.cs236369.hw5.core.utils.OperationResult;

import com.google.gson.Gson;

public enum UserOperation {
	
	USER_INSERT {
		public UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager();
			EntityTransaction transaction = manager.getTransaction( );
			transaction.begin( );
			User user = collectValues(request);
			try {
				String roleName = request.getParameter( "role");
				if ( roleName == null)
					roleName = "undefined";
				Role role = manager.find( Role.class, roleName);
				if ( role == null) {
					role = new Role( );
					role.setRole(roleName);
					role.setLevel(1);
					manager.persist(role);
				}
				user.setPermission(role);
				manager.persist(user);
				transaction.commit( );
				status.set( OperationResult.SUCCESS);
			} catch ( Exception e) {
				transaction.rollback( ); 
				status.set( OperationResult.ERROR);
				logger.error( e.getMessage( ));
			} finally {
				manager.close( );
			}
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			defaultOutputImpl(response);
			return this;
		}
	},
	USER_UPDATE {
		public  UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager( );
			EntityTransaction transaction = manager.getTransaction( );
			transaction.begin( );
			try {
				User user = manager.find( User.class, request.getParameter( UserParameters.USER_LOGIN.getName( )));
				for( UserParameters parameter : UserParameters.values( )) {
					if ( request.getParameter( parameter.getName( )) != null) {
						parameter.saveParameterValue(user, request.getParameter( parameter.getName( )));
					}
				}
				String roleName = request.getParameter( "role");
				Role role = manager.find( Role.class, roleName);
				if ( role == null) {
					role = new Role( );
					role.setRole(roleName);
					role.setLevel(1);
					manager.persist(role);
				}
				user.setPermission(role);
				manager.merge( user);
				transaction.commit( );
				status.set( OperationResult.SUCCESS);
			} catch ( Exception e) {
				transaction.rollback( );
				status.set( OperationResult.ERROR);
				logger.error( e.getMessage( ));
			} finally {
				manager.close( );
			}
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			defaultOutputImpl(response);
			return this;
		}
	},
	USER_DELETE {
		public UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager( );
			EntityTransaction transaction = manager.getTransaction( );
			transaction.begin( );
			try {
				User user = collectValues(request);
				manager.remove( manager.find( User.class, user.getLoginName( )));
				transaction.commit( );
				status.set( OperationResult.SUCCESS);
			} catch ( Exception e) {
				logger.error( e.getMessage( ));
				transaction.rollback( );
				status.set( OperationResult.ERROR);
			} finally {
				manager.close( );
			}
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			defaultOutputImpl(response);
			return this;
		}
	},
	USER_LOGIN_AVIABILITY {
		public UserOperation execute( HttpServletRequest request) {
			String login = request.getParameter( "login");
			if ( login == null || login.length( ) == 0) {
				status.set( OperationResult.ERROR);
				return this;
			}
			EntityManager manager = emf.createEntityManager( );
			User user = manager.find( User.class, login);
			if ( user != null)
				status.set( OperationResult.ERROR);
			else
				status.set( OperationResult.SUCCESS);
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			defaultOutputImpl(response);
			return this;
		}
	},
	USER_GET_WAITING_LIST{
		protected ThreadLocal<List<User>> users = new ThreadLocal< List<User>>( );
		
		@SuppressWarnings("unchecked")
		public UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager( );
			Query query = manager.createQuery( "SELECT u FROM User u WHERE u.activityStatus is NULL");
			users.set( query.getResultList( ));
			status.set( OperationResult.SUCCESS);
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			switch(status.get( )) {
				case SUCCESS: { 
					Gson gson = new Gson( );
					try {
						UserItems items = new UserItems( );
						for (User user : users.get ()) {
							items.getItems().add( new UserEnvelope( user.getLoginName(), user.getFirstName(),
									user.getLastName(), user.getPhoneNumber(), user.getActivityStatus( ),
									//Before insert user role need to ensure it was defined previously
									(user.getPermission() != null)?user.getPermission().getRole( ):null));
						}
						response.getWriter( ).println( gson.toJson(items));
					} catch (IOException e) {
						logger.error( e.getMessage( ));
					}
					logger.info( "Reporting successeful enf of " + toString( ));
					break;
				}
				case ERROR: {
					logger.info( "Reporting failure enf of " + toString( ));
					break;
				}
			}
			return this;
		}
	}, 
	USER_GET_ACTIVE_LIST{
		protected ThreadLocal< List<User>> users = new ThreadLocal<List<User>>( );
		
		@SuppressWarnings("unchecked")
		public UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager( );
			Query query = manager.createQuery( "SELECT u FROM User u WHERE u.activityStatus >= CURRENT_DATE");
			users.set(query.getResultList());
			status.set( OperationResult.SUCCESS);
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			switch( status.get( )) {
				case SUCCESS: {
					Gson gson = new Gson( );
					try {
						UserItems items = new UserItems( );
						for (User user : users.get( )) {
							items.getItems().add( new UserEnvelope( user.getLoginName(), user.getFirstName(),
									user.getLastName(), user.getPhoneNumber(), user.getActivityStatus( ),
									//Before insert user role need to ensure it was defined previously
									(user.getPermission() != null)?user.getPermission().getRole( ):null));
						}
						response.getWriter( ).println( gson.toJson(items));
					} catch (IOException e) {
						e.printStackTrace();
						logger.error( e.getMessage( ));
					}
					logger.info( "Reporting success of " + toString( ));
					break;
				}
				case ERROR: {
					logger.info( "Reporting failure of " + toString( ));
					break;
				}
			}
			return this;
		}
	}, 
	USER_GET_ALL_LIST{
		protected ThreadLocal< List<User>> users = new ThreadLocal<List<User>>( );
		
		@SuppressWarnings("unchecked")
		public UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager( );
			Query query = manager.createQuery( "SELECT u FROM User u");
			users.set(query.getResultList());
			status.set( OperationResult.SUCCESS);
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			switch( status.get( )) {
				case SUCCESS: {
					Gson gson = new Gson( );
					try {
						UserItems items = new UserItems( );
						for (User user : users.get( )) {
							items.getItems().add( new UserEnvelope( user.getLoginName(), user.getFirstName(),
									user.getLastName(), user.getPhoneNumber(), user.getActivityStatus( ),
									//Before insert user role need to ensure it was defined previously
									(user.getPermission() != null)?user.getPermission().getRole( ):null));
						}
						response.getWriter( ).println( gson.toJson(items));
					} catch (IOException e) {
						e.printStackTrace();
						logger.error( e.getMessage( ));
					}
					logger.info( "Reporting success of " + toString( ));
					break;
				}
				case ERROR: {
					logger.info( "Reporting failure of " + toString( ));
					break;
				}
			}
			return this;
		}
	},
	USER_GET_DISABLED_LIST{
		private ThreadLocal< List<User>> users = new ThreadLocal<List<User>>( );
		@SuppressWarnings("unchecked")
		public UserOperation execute( HttpServletRequest request) {
			EntityManager manager = emf.createEntityManager( );
			Query query = manager.createQuery( "SELECT u FROM User u WHERE u.activityStatus < CURRENT_DATE");
			users.set( query.getResultList( ));
			status.set( OperationResult.SUCCESS);
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			switch( status.get( )) {
				case SUCCESS: {
						Gson gson = new Gson( );
						try {
							UserItems items = new UserItems( );
							for (User user : users.get( )) {
								items.getItems().add( new UserEnvelope( user.getLoginName(), user.getFirstName(),
										user.getLastName(), user.getPhoneNumber(), user.getActivityStatus( ),
										//Before insert user role need to ensure it was defined previously
										(user.getPermission() != null)?user.getPermission().getRole( ):null));
							}
							response.getWriter( ).println( gson.toJson(items));
						} catch (IOException e) {
							logger.error( e.getMessage( ));
						}
						logger.info( "Reporting success of: " + toString( ));
						break;
					}
				case ERROR : {
					logger.info( "Reporting failure of: " + toString( ));
					break;
				}
			}
			return this;
		}
	},
	UNDEFINED {
		public UserOperation execute( HttpServletRequest request) {
			return this;
		}
		
		public UserOperation resultOutput( HttpServletResponse response) {
			try {
				response.getWriter( ).println( "ERROR");
				logger.warn( "Undefined operation");
			} catch (IOException e) {
				logger.error( e.getMessage( ));
			}
			return this;
		}
	};
	
	protected ThreadLocal< OperationResult> status = new ThreadLocal<OperationResult>( ) {
		@Override
		protected OperationResult initialValue( ) {
			return OperationResult.SUCCESS;
		}
	};
	
	protected EntityManagerFactory emf = Persistence.createEntityManagerFactory( "jpaProvider");
	
	private static final Map<String, UserOperation> map = new HashMap<String, UserOperation>( );
	
	protected static final Logger logger = Logger.getLogger( UserOperation.class);
	
	static {
		for ( UserOperation operation : values( )) {
			map.put( operation.name().toUpperCase( ), operation);
		}
	}
	
	public static final UserOperation fromString( final String name) {
		UserOperation $ = map.get(name);
		if ( $ == null) {
			logger.warn( "Operation " + name + " is undefined.");
			return UserOperation.UNDEFINED;
		}
		return $;
	}
	
	public User collectValues( HttpServletRequest request) {
		User $ = new User( );
		for( UserParameters parameter : UserParameters.values( )) {
			parameter.saveParameterValue($, request.getParameter(parameter.getName( )));
		}
		return $;
	}
	
	abstract public UserOperation execute( HttpServletRequest request);
	
	abstract public UserOperation resultOutput( HttpServletResponse response);
	
	protected void defaultOutputImpl( HttpServletResponse response) {
		try {
			switch( status.get( )) {
				case SUCCESS : {
					response.getWriter().println( "SUCCESS");
					break;
				}
				case ERROR : {
					response.getWriter().println( "ERROR");
					break;
				}
			}
		} catch (IOException e) {
			logger.error( e.getMessage( ));
		}
	}
}
